#encoding:utf8

import sys
import struct
import random
import math
import thread 
import time
import socket 
import re

try:
	from OpenGL.GLUT import *
	from OpenGL.GL import *
	from OpenGL.GLU import *
except:
	print """
ERROR: PyOpenGL not installed properly.  
		"""
	sys.exit()

x_coord		= 10
y_coord		= 10
isAserver	= True #True为服务端，False为客户端

#数值统统为4个字节，最高位为符号位，1600万足矣,低字节为低位
def westicenumtostring(num):
	tempnum =abs(num)
	tempnum =int(tempnum)
	s =""
	i =0
	while tempnum>0:
		s=s+chr(tempnum%256)
		tempnum=int(tempnum/256) 
		i+=1
	while len(s)<3:#补齐3位
		s=s+chr(0)
	if num<0:
		s=s+chr(1)
	else:#0和正数最高位为0
		s=s+chr(0)
	return s

def westicestringtonum(s):
	if len(s)!=4:
		print "len(s) must be 4"
		return None
	n0=ord(s[0])
	n1=ord(s[1])
	n2=ord(s[2])
	n3=ord(s[3])
	num=n0+256*n1+256*256*n2
	if n3==1:#负数
		num=(-num)
	if n3!=0 and n3!=1:
		return "wrong string when convert ro number"
	return num

def drawenemy(x,y): #画敌人形象
	glLoadIdentity()
	glTranslate(x,y,0.0)
	glScale(0.2,0.2,0.2)

	glBegin(GL_POLYGON)  
	glVertex3f(1.0,1.0,0)
	glVertex3f(1.0,-1.0,0)
	glColor3f(1.0,0.0,0.0)
	glVertex3f(-1.0,-1.0,0)
	glVertex3f(-1.0,1.0,0)
	glEnd()

def drawbullet(x,y):#画子弹形象
	glLoadIdentity()
	glTranslate(x, y, 0.0)
	glScale(0.1, 0.1, 0.1)

	glColor3f(0.0, 1.0, 0.0)		   

	glBegin(GL_TRIANGLES)
	glVertex3f(-.5, 1, 0)
	glVertex3f(0, 1.5, 0)
	glVertex3f(.5, 1, 0)
	glEnd()

	glColor3f(0.0, 0, 0.8)
	glRectf(.5, 1, -.5, -1)

def drawpalne(x, y, rquad):#画玩家飞机形象
	glLoadIdentity()
	glTranslate(x,y,0.0)
	glRotatef(rquad,0.0,1.0,0.0)		# Rotate 
	glScale(0.5,0.5,0.5)

	glBegin(GL_POLYGON)					  # Start drawing 
	glColor3f(1.0,0.0,0.0)
	glVertex3f(1.0,0.0,0.0)		  
	glColor3f(0.0,1.0,0.0)
	glVertex3f(-1.0,0.0,0.0)		 
	glColor3f(0.0,0.0,1.0)
	glVertex3f(0.0,3.0,0.0)			
	glEnd()								# We are done with the polygon

	glBegin(GL_POLYGON)  
	glVertex3f(0.0,0.0,0)
	glVertex3f(0.0,3.0,0)
	glColor3f(1.0,0.0,0.0)
	glVertex3f(0.0,0.5,0.5)
	glEnd()

class Enemy(): #定义敌人
	def __init__(self):
		self.reset()
	def update(self):
		if self.live:
			self.y-=0.01
			self.draw()
			if self.y<0:
				self.live=False 
	def setxy(self,x,y):
		self.x=x
		self.y=y
	def draw(self):
		drawenemy(self.x,self.y)
	def reset(self):
		self.x=x_coord*random.random()
		self.y=y_coord
		self.live=True #活着状态

class Bullet():#定义子弹
	def __init__(self,x,y):
		self.reset(x,y)
		self.live =False

	def update(self):
		if self.live:
			self.y+=0.05
			self.draw()
			if self.y>y_coord:
				self.live=False			 
	def draw(self):
		drawbullet(self.x,self.y)

	def reset(self,x,y):
		self.x=x
		self.y=y
		self.live=True

class Plane():
	def __init__(self,x,y):
		self.x=x
		self.y=y
		self.rquad=0
	def update(self):
		self.draw()
		if self.rquad<0:
			self.rquad+=1.0
		if self.rquad>0:
			self.rquad-=1.0
	def draw(self):
		drawpalne(self.x,self.y,self.rquad)

	def left(self):
		self.x-=0.1
		if self.rquad>-40:#限制
			self.rquad-=3
	def right(self):
		self.x+=0.1
		if self.rquad<40:
			self.rquad+=3

westiceplane=None
myenemylist=None
bulletlist=None
otherplane=None
otherenemylist=None
otherbulletlist=None
frameobj=None
#网络用帧
class netframe():
	def __init__(self,player,mybulletlist,enemylist):
		self.player=player
		self.mybulletlist=mybulletlist
		self.enemylist=enemylist
	def senddata(self,conn):
		global isconnected
		senddata=""
		senddata+=westicenumtostring(self.player.x*1000)
		senddata+=westicenumtostring(self.player.y*1000) 
		senddata+=westicenumtostring(self.player.rquad*1000)#自己的位置
		senddata+=westicenumtostring(len(self.mybulletlist))#子弹个数
		for bullet in self.mybulletlist:
			senddata+=westicenumtostring(bullet.x*1000)
			senddata+=westicenumtostring(bullet.y*1000)#子弹位置
		senddata+=westicenumtostring(len(self.enemylist))#敌人个数
		for enemy in self.enemylist:
			senddata+=westicenumtostring(enemy.x*1000)
			senddata+=westicenumtostring(enemy.y*1000)#敌人位置
		if isconnected:
			try:		
				conn.send(senddata)
			except socket.error:
				isconnected=False
		#conn.sendall(senddata)
	#接收数据
	def recvdata(self,conn):
		global otherplane
		global otherenemylist
		global otherbulletlist
		global isconnected
		if isconnected:
			try:
				recvdata=conn.recv(4)
				otherplanex=westicestringtonum(recvdata)/1000.0
				recvdata=conn.recv(4)
				otherplaney=westicestringtonum(recvdata)/1000.0
				otherplane=Plane(otherplanex,otherplaney)
				recvdata=conn.recv(4)
				otherplane.rquad=westicestringtonum(recvdata)/1000.0
				recvdata=conn.recv(4)#接收子弹数据
				bulletnum=westicestringtonum(recvdata)
				otherbulletlist=[]
				for i in range(bulletnum):
					recvdata=conn.recv(4)
					bulletx=westicestringtonum(recvdata)/1000.0
					recvdata=conn.recv(4)
					bullety=westicestringtonum(recvdata)/1000.0
					bullet=Bullet(bulletx,bullety)
					bullet.live=True
					otherbulletlist.append(bullet)

				recvdata=conn.recv(4)
				enemynum=westicestringtonum(recvdata)
				otherenemylist=[]
				for i in range(enemynum):
					recvdata=conn.recv(4)
					enemyx=westicestringtonum(recvdata)/1000.0
					recvdata=conn.recv(4)
					enemyy=westicestringtonum(recvdata)/1000.0
					enemy=Enemy()	  
					enemy.setxy(enemyx,enemyy)
					otherenemylist.append(enemy)
			except socket.error:
				isconnected=False

#游戏服务器
port=8088  #主数据通道
conn=None#socket连接对象，可能是服务器或客户端
serverUDPport=9090	
clientTCPport=9091	
isconnected=False
class GameServer():
	def __init__(self):
		print u"初始化游戏服务器"
		global conn
		global otherplane
		global otherenemylist
		global otherbulletlist
		global frameobj
		global isconnected
		global serverUDPport
		global clientTCPport

		#服务器接收客户UDP报文，如果验证通过向客户发送TCP回应
		UDPsock0=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)	
		UDPsock0.bind(("",serverUDPport))
		#还需验证客户端,用udp
		recvstring,clientaddr=UDPsock0.recvfrom(1024)
		if recvstring=="west":
			print "服务器收到验证from ",clientaddr[0]
		UDPsock0.close()

		#服务器发送tcp回应
		tempconn=socket.socket(socket.AF_INET,socket.SOCK_STREAM)  
		tempconn.connect((clientaddr[0],clientTCPport))
		tempconn.send("sure")
		tempconn.close()

		#创建TCP服务器
		TCPsock=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
		TCPsock.bind(("",port))
		TCPsock.listen(5)		 
		conn,clientaddr=TCPsock.accept()
		print "来自客户端：",clientaddr
		isconnected=True	
		while isconnected: #服务端循环，先发后收
			frameobj.senddata(conn)
			time.sleep(0.02)
			frameobj.recvdata(conn)
		conn.close()

#游戏客户端
class GameClient():
	def __init__(self):
		print "初始化游戏客户端"
		global conn
		global frameobj
		global isconnected
		global serverUDPport
		global clientTCPport
		self.lanserverip=None
		#搜索服务器，向局域网内发送udp数据
		ip			= socket.gethostbyname(socket.gethostname())
		print(ip)
		match_str	= r"\d+\.\d+\.\d+\.\d+"
		ipheader	= re.match(match_str, ip)
		ipheader	= ipheader.group()
		print(ipheader)
		UDPsock0	=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)	
		for i in range(1,256):
			self.lanserverip=ipheader+str(i)
			UDPsock0.sendto("west",(self.lanserverip,serverUDPport))
		UDPsock0.close()

		#客户端建立tcp服务器  接收服务器ip和其它信息，之后断开，连接服务器的tcp
		TCPsock=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
		TCPsock.bind(("",clientTCPport))
		TCPsock.listen(5)	 
		tempconn,serveraddr=TCPsock.accept()
		hellomsg=tempconn.recv(4)
		tempconn.close()
		self.lanserverip=serveraddr[0]
		print u"服务器为：",self.lanserverip
		print u"服务器信息：",hellomsg

		#连接tcp服务器发送数据
		conn=socket.socket(socket.AF_INET,socket.SOCK_STREAM)  
		conn.connect((self.lanserverip,port))
		isconnected=True
		while isconnected:#客户端循环，先收后发
			frameobj.recvdata(conn)
			frameobj.senddata(conn)
			time.sleep(0.02)
		conn.close()

def threadfunc():#线程函数
	global isAserver
	if isAserver:
		gameserver=GameServer()
	else:
		gameclient=GameClient()

def GameInit():
	global x_coord
	global y_coord
	global westiceplane
	global myenemylist
	global mybulletlist
	global frameobj

	westiceplane= Plane(x_coord/2,1) #加入飞机
	myenemylist=[]
	for i in range(4):	#加入敌人
		westiceenemy=Enemy()
		myenemylist.append(westiceenemy)
	mybulletlist=[]

	for i in range(20):
		mywesticebullet =Bullet(westiceplane.x,westiceplane.y)
		mybulletlist.append(mywesticebullet)

	#新建一个线程处理网络
	thread.start_new_thread(threadfunc,())
	frameobj=netframe(westiceplane,mybulletlist,myenemylist)

def init():
	glClearColor(0, 0, 0.6, 0)
	glClearDepth(1.0)					 # Enables Clearing Of The Depth Buffer
	glDepthFunc(GL_LESS)				# The Type Of Depth Test To Do
	glEnable(GL_DEPTH_TEST)				   # Enables Depth Testing
	glShadeModel(GL_SMOOTH)				   # Enables Smooth Color Shading

def calcdistance(object0, object1):
	return math.sqrt(pow(object0.x-object1.x,2)+pow(object0.y-object1.y,2))

count = 0
def display():
	glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)

	westiceplane.update()

	for enemy in myenemylist:
		enemy.update()
		if not enemy.live:
			enemy.reset() 
		for bullet in mybulletlist:
			if calcdistance(bullet, enemy) <= 0.2: #是否相遇
				bullet.live	= False
				enemy.live	= False

	#自动发射子弹
	global count
	if count>=15:
		count=0

		for bullet in mybulletlist:
			if not bullet.live:
				bullet.reset(westiceplane.x, westiceplane.y+1 ) #激活一颗
				break

	for bullet in mybulletlist:
		bullet.update()    
	count+=1

	#显示网络来的数据
	global otherplane
	global otherenemylist
	global otherbulletlist
	if isconnected and otherplane:
		otherplane.draw()
	
	if isconnected and otherbulletlist and otherenemylist and otherplane:
		otherplane.draw()
		for myenemy in myenemylist:
			for otherbullet in otherbulletlist:
				if calcdistance(otherbullet,myenemy)<=0.2:#是否相遇
					otherbullet.live=False	 
					myenemy.live=False	
		for otherenemy in otherenemylist:
			if otherenemy.live:
				otherenemy.draw()
		for otherbullet in otherbulletlist:
			if otherbullet.live:
				otherbullet.draw()

	glutSwapBuffers()
	#glFlush()

def reshape(width, height):
	width	= width or 1
	height	= height or 1
	glViewport(0, 0, width, height)
	glMatrixMode(GL_PROJECTION)
	glLoadIdentity()
	print("width:", width, "height:", height)

	global x_coord
	global y_coord

	if width < height:
		gluOrtho2D(0, x_coord, 0, x_coord * height / width)
	else:
		gluOrtho2D(0, x_coord * width / height, 0, x_coord)
	glMatrixMode(GL_MODELVIEW)

def keyboard(key, x, y):
	# esc键退出
	if key==chr(27):
		sys.exit(0)

	key	= key.lower()
	if key=="a":
		westiceplane.left()
	elif key=="d":
		westiceplane.right()


GameInit()
glutInit(sys.argv)
glutInitDisplayMode(GLUT_RGBA|GLUT_DOUBLE|GLUT_DEPTH) #使用双缓冲和深度测试
glutInitWindowSize(600,500) 
glutInitWindowPosition(100,100)

title	= isAserver and 'Game Server' or 'Game Client'
glutCreateWindow(title)

init()

glutReshapeFunc(reshape)
glutKeyboardFunc(keyboard)
glutDisplayFunc(display)
glutIdleFunc(display)

glutMainLoop()
